package ffte.types

import spinal.core._
import ffte.property.getResolution

/**
 * Fixed-point real number implementation
 *
 * This class implements a fixed-point real number as a SpinalHDL Bundle using
 * SFix for the underlying representation. It provides comprehensive arithmetic
 * operations, precision management, and utility functions optimized for hardware
 * implementation in FFT systems.
 *
 * Data Representation:
 * - Signed fixed-point using SpinalHDL SFix
 * - Configurable bit width and fractional resolution
 * - Two's complement representation
 * - Automatic saturation handling for conversions
 *
 * Key Features:
 * - Basic arithmetic operations (+, -, *, unary minus)
 * - Precision management (resize, fixTo, cut, sat)
 * - Trigonometric functions (cos, sin) for twiddle factors
 * - Conversion utilities (fromInt, fromDouble)
 * - Complex number formation (j operator)
 *
 * Hardware Optimizations:
 * - Efficient bit manipulation for precision control
 * - Hardware-friendly trigonometric generation
 * - Automatic bit growth management
 * - Resource-efficient implementations
 *
 * @param width Total bit width for the fixed-point number
 * @param resolution Fractional bit resolution (defaults to global setting)
 */
case class FixReal(val width:Int,val resolution:Int=getResolution()) extends Bundle {
    /** Underlying SpinalHDL signed fixed-point representation */
    val d = SFix((width-1+resolution) exp, resolution exp)
    /** Internal representation of 1.0 in integer format */
    def iOne = (1<<(-resolution-1))

    /** Maximum positive integer value for this bit width */
    def iMax = (1<<width)-1
    /**
     * Resize to specified bit width
     *
     * Changes the bit width while maintaining the same fractional resolution.
     * Uses simple truncation/extension without saturation handling.
     *
     * @param dW New bit width
     * @return FixReal with new bit width
     */
    def resize(dW:Int) = {
        val r = FixReal(dW,resolution)
        r.d.raw := this.d.raw.resize(dW)
        r
    }
    /** Expand bit width by 1 bit */
    def expand = this.resize(width+1)
    /**
     * Addition with truncation
     *
     * Adds two fixed-point numbers and truncates the result to prevent
     * overflow while maintaining the original bit width.
     *
     * @param right Addend
     * @return Sum with truncated result
     */
    def + (right: FixReal): FixReal = {
        val ret = FixReal(width,resolution)
        ret.d := (this.d + right.d).truncated
        ret
    }
    /**
     * Subtraction
     *
     * Subtracts two fixed-point numbers without truncation.
     * Result bit width may grow to prevent overflow.
     *
     * @param right Subtrahend
     * @return Difference
     */
    def - (right: FixReal): FixReal = {
        val ret = FixReal(width,resolution)
        ret.d := this.d - right.d
        ret
    }
    /**
     * Multiplication with bit growth
     *
     * Multiplies two fixed-point numbers with automatic bit width and
     * resolution growth to prevent overflow and maintain precision.
     *
     * @param right Multiplier
     * @return Product with expanded bit width and resolution
     */
    def * (right: FixReal): FixReal = {
        val ret = FixReal(this.width+right.width,this.resolution+right.resolution)
        ret.d.raw := this.d.raw*right.d.raw
        ret
    }
    def unary_- : FixReal = {
        val ret = FixReal(width,resolution)
        ret.d.raw := -this.d.raw.symmetry
        ret
    }
    def j(that: FixReal) : FixComplex = {
        val ret = FixComplex(width,resolution)
        ret.re := this
        ret.im := that
        ret
    }
    def :=(that: SFix): Unit     = {
        this.d := that
    }
    def abs : UInt = {
        this.d.raw.absWithSym 
    }
    def zero = {
        this.d.raw := S(0)
        this
    }
    def fromInt(x:Int) = {
        this.d.raw := S(x)
        this
    }
    def fromDouble(x:Double) = {
        val d = Math.round(x*iOne).toInt
        val satd =  if(d>iMax) iMax 
                    else if (d < -iMax) -iMax 
                    else d
        this.d.raw := S(satd)
    }
    def sat(n:Int) = {
        val ret = FixReal(width,resolution)
        ret.d.raw := this.d.raw - (this.d.raw|>>n)
        ret
    }
    def fixTo(w:Int,s:Int) = {
        val ret = FixReal(w,resolution)
        ret.d.raw := this.d.raw.fixTo(w+s-1 downto s, RoundType.ROUNDTOINF,sym=true)
        ret
    }
    def fixTo(w:Int,s:Int,r:Int) = {
        val ret = FixReal(w,r)
        ret.d.raw := this.d.raw.fixTo(w+s-1 downto s, RoundType.ROUNDTOINF,sym=true)
        ret
    }
    def cut(w:Int,s:Int) = {
        val ret = FixReal(w,resolution)
        ret.d.raw := this.d.raw(w+s-1 downto s)
        ret
    }
    def cut(w:Int,s:Int,r:Int) = {
        val ret = FixReal(w,r)
        ret.d.raw := this.d.raw(w+s-1 downto s)
        ret
    }
    def cos(m:Int,N:Int) = {
        val ret = FixReal(width,resolution)
        val cos = Math.round(Math.cos(m*2*Math.PI/N)*iOne).toInt
        ret.d.raw := cos
        ret
    }
    def sin(m:Int,N:Int) = {
        val ret = FixReal(width,resolution)
        val sin = Math.round(Math.sin(m*2*Math.PI/N)*iOne).toInt
        ret.d.raw := sin
        ret
    }
    def one = {
        val ret = FixReal(width,resolution)
        ret.d.raw := iOne
        ret
    }
}
